{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "vscode": {
     "languageId": "shellscript"
    }
   },
   "outputs": [],
   "source": [
    "! g++ -std=c++20 -O3 -march=native -Wall -Wextra mem_pool_bench.cpp -lbenchmark -lpthread -o mem_pool_benches"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "vscode": {
     "languageId": "shellscript"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2025-10-09T23:06:59+08:00\n",
      "Running ./mem_pool_benches\n",
      "Run on (16 X 3792.92 MHz CPU s)\n",
      "CPU Caches:\n",
      "  L1 Data 32 KiB (x8)\n",
      "  L1 Instruction 32 KiB (x8)\n",
      "  L2 Unified 1024 KiB (x8)\n",
      "  L3 Unified 16384 KiB (x1)\n",
      "Load Average: 0.24, 0.21, 0.14\n",
      "---------------------------------------------------------------------------\n",
      "Benchmark                 Time             CPU   Iterations UserCounters...\n",
      "---------------------------------------------------------------------------\n",
      "BM_LegacyMemPool      64867 ns        64866 ns        10462 items_per_second=0/s\n",
      "BM_ModernMemPool       7583 ns         7583 ns        93190 items_per_second=0/s\n"
     ]
    }
   ],
   "source": [
    "! ./mem_pool_benches"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "vscode": {
     "languageId": "shellscript"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2025-10-09T23:07:27+08:00\n",
      "Running ./mem_pool_benches\n",
      "Run on (16 X 3792.92 MHz CPU s)\n",
      "CPU Caches:\n",
      "  L1 Data 32 KiB (x8)\n",
      "  L1 Instruction 32 KiB (x8)\n",
      "  L2 Unified 1024 KiB (x8)\n",
      "  L3 Unified 16384 KiB (x1)\n",
      "Load Average: 0.15, 0.19, 0.13\n",
      "----------------------------------------------------------------------------------\n",
      "Benchmark                        Time             CPU   Iterations UserCounters...\n",
      "----------------------------------------------------------------------------------\n",
      "BM_LegacyMemPool_mean        64978 ns        64977 ns           10 items_per_second=0/s\n",
      "BM_LegacyMemPool_median      64785 ns        64784 ns           10 items_per_second=0/s\n",
      "BM_LegacyMemPool_stddev        686 ns          686 ns           10 items_per_second=0/s\n",
      "BM_LegacyMemPool_cv           1.06 %          1.06 %            10 items_per_second=-nan%\n",
      "BM_ModernMemPool_mean         9034 ns         9034 ns           10 items_per_second=0/s\n",
      "BM_ModernMemPool_median       9028 ns         9028 ns           10 items_per_second=0/s\n",
      "BM_ModernMemPool_stddev       37.1 ns         37.1 ns           10 items_per_second=0/s\n",
      "BM_ModernMemPool_cv           0.41 %          0.41 %            10 items_per_second=-nan%\n"
     ]
    }
   ],
   "source": [
    "! ./mem_pool_benches --benchmark_repetitions=10 --benchmark_report_aggregates_only=true --benchmark_filter=.*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# legacy_mem_pool"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "/**\n",
    " * @brief 回收指定指针指向的对象槽位。\n",
    " *\n",
    " * @param elem 指向池内对象的指针。\n",
    " * @note 仅将槽位标记为空闲，不调用对象析构函数；使用者需要自行管理资源释放。\n",
    " */\n",
    "auto deallocate(const T* elem) noexcept {\n",
    "    // 将 T* 强制解释为 ObjectBlock*，然后通过指针差值计算索引。\n",
    "    const auto elem_index = (reinterpret_cast<const ObjectBlock*>(elem) - &store_[0]);\n",
    "\n",
    "    // 断言：检查待回收指针是否确实来自当前内存池。\n",
    "    ASSERT(elem_index >= 0 && static_cast<size_t>(elem_index) < store_.size(),\n",
    "           \"Element being deallocated does not belong to this Memory pool.\");\n",
    "    // 断言：确保该槽位当前处于“已占用”状态，防止重复回收导致逻辑错误。\n",
    "    ASSERT(\n",
    "        !store_[elem_index].is_free_,\n",
    "        \"Expected in-use ObjectBlock at index:\" + std::to_string(elem_index));\n",
    "\n",
    "    store_[elem_index].is_free_ = true;  // 仅重置空闲标记\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`deallocate()` 里只是把 `is_free_` 置回 `true`，并没有真正销毁对象——也就是说：\n",
    "\n",
    "- **对象的字节内容还原封不动地留在那块内存里**，不会“消失”或“清零”；\n",
    "- 更重要的是，对象的**生命周期并没有结束**，因为我们没有调用析构函数。\n",
    "\n",
    "如果随后再次调用 `allocate()` 并在这块内存上执行 `placement new`，那么在旧对象未析构的情况下直接构造新对象会导致 **未定义行为**（标准要求必须先显式调用析构函数，然后才能在同一地址构造新对象）。只有在 `T` 是平凡析构（trivially destructible）类型时，才可以略过析构而不出问题；否则会造成资源泄漏或逻辑错误。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "auto deallocate(const T* elem) noexcept {\n",
    "  const auto block = std::launder(reinterpret_cast<ObjectBlock*>(\n",
    "                   const_cast<T*>(elem))); // 确保严格别名、安全访问\n",
    "\n",
    "  const auto elem_index = block - &store_[0];\n",
    "\n",
    "  ASSERT(elem_index >= 0 && static_cast<std::size_t>(elem_index) < store_.size(),\n",
    "         \"Element being deallocated does not belong to this Memory pool.\");\n",
    "  ASSERT(!store_[elem_index].is_free_,\n",
    "         \"Expected in-use ObjectBlock at index:\" + std::to_string(elem_index));\n",
    "\n",
    "  std::destroy_at(&block->object_);  // 手动调用 T 的析构函数\n",
    "  block->is_free_ = true;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `std::launder`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| 关键点   | 说明                                                         |\n",
    "| -------- | ------------------------------------------------------------ |\n",
    "| 头文件   | `<new>`                                                      |\n",
    "| 引入版本 | C++17                                                        |\n",
    "| 用途     | 获取在同一地址重新构造对象后的“新指针”；避免 UB 和优化器错误假设。 |\n",
    "| 常见搭配 | `placement new`, `std::destroy_at`, `std::uninitialized_*` 系列函数。 |\n",
    "| 慎用场景 | 指向未构造对象、类型不匹配或 `nullptr` 指针上调用 `std::launder` 均属未定义行为。 |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`std::launder` 是 `<new>` 头文件中的函数模板，于 **C++17** 引入，专门用于在以下场景中安全地取得对象指针：\n",
    "\n",
    "- 使用 **placement new** 在一块已经存储过其它对象的内存上重新构造（placement-new *同址重建*）。\n",
    "- 动态修改对象类型（例如利用 `union`、`std::aligned_storage` 等实现类型擦除或自定义容器）。\n",
    "- 通过指针访问对象的成员并依赖标准对对象表示的严格规则时（避免违反严格别名、对象生命周期规则）。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "为什么需要 `std::launder`？\n",
    "\n",
    "在 C++ 的对象模型中，当你在某个地址使用 placement new 构造“新的对象”后，**之前的对象生命周期已经结束**。<br/>\n",
    "标准要求：**如果没有取得新对象的指针而继续使用旧指针，行为未定义。**\n",
    "\n",
    "`std::launder` 就是为了解决这种“老指针指向新对象”的情况。它通过一个轻量的编译器内建（通常只是返回传入的指针），告诉编译器“我现在要把这个地址重新解释为一个合法的对象指针”，从而避免优化器基于旧对象状态做出错误推断。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "100"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "{\n",
    "  alignas(X) unsigned char buffer[sizeof(X)];\n",
    "  X* px = new (buffer) X{42}; // 首次构造\n",
    "\n",
    "  px->~X();                   // 显式析构\n",
    "  new (px) X{100};            // 在同一地址重新构造\n",
    "\n",
    "  // 再使用原始指针 px 访问新对象是未定义行为（标准要求使用 std::launder）\n",
    "  X* px_reborn = px;\n",
    "  return px_reborn->value;    // OK，返回 100\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "100"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#include <new>\n",
    "#include <memory>\n",
    "#include <type_traits>\n",
    "\n",
    "struct X {\n",
    "  int value;\n",
    "};\n",
    "\n",
    "{\n",
    "  alignas(X) unsigned char buffer[sizeof(X)];\n",
    "  X* px = new (buffer) X{42}; // 首次构造\n",
    "\n",
    "  px->~X();                   // 显式析构\n",
    "  new (px) X{100};            // 在同一地址重新构造\n",
    "\n",
    "  // 再使用原始指针 px 访问新对象是未定义行为（标准要求使用 std::launder）\n",
    "  X* px_reborn = std::launder(px);\n",
    "  return px_reborn->value;    // OK，返回 100\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "常见误解\n",
    "\n",
    "1. **“placement new 返回的指针还需要 launder 吗？”**<br/>\n",
    "不需要。`new (ptr) T(...)` 的返回值就是合法指向新对象的指针，直接用这个指针即可。<br/>\n",
    "需要 `std::launder` 的是“之前保存下来的旧指针”。\n",
    "\n",
    "2. **“std::launder 会执行额外操作吗？”**<br/>\n",
    "通常不会，绝大多数实现里它只是原封不动返回同一个地址。但语义上，它使得代码与标准的对象模型保持一致，从而避免未定义行为。\n",
    "\n",
    "3. **“任何指针都能 launder 吗？”**<br/>\n",
    "不行。指针必须已经指向合法对象或指向其子对象。如果对象生命周期未开始，调用 `std::launder` 同样是未定义行为。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "与内存池/自定义容器的关系\n",
    "\n",
    "在手写内存池时，常见流程是：\n",
    "\n",
    "1. 使用 `reinterpret_cast` 将内存块视为对象指针；\n",
    "2. `placement new` 在该内存块上构造对象；\n",
    "3. 记录并返回构造好的指针；\n",
    "4. 回收时析构并把槽位标记为空闲。\n",
    "\n",
    "若你保留了对象指针并在析构后再次使用（例如访问成员、重新构造），则需要 `std::launder` 来“刷新”指针。\n",
    "\n",
    "- `std::launder` 用来确保经过 `reinterpret_cast` 后的指针满足标准对象模型的要求。\n",
    "- `std::destroy_at` 确保析构正确调用。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "C++17",
   "language": "C++17",
   "name": "xcpp17"
  },
  "language_info": {
   "codemirror_mode": "text/x-c++src",
   "file_extension": ".cpp",
   "mimetype": "text/x-c++src",
   "name": "c++",
   "version": "17"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
